UserLand Software
Powerful, cross-platform web scripting.
 

Defines and Custom Directives

Frontier Web Tutorial

About This Tutorial

About Web Site Management

Why Frontier?

Starting Up

Getting Comfortable With Tables

Exploring the Examples

Your First Web Site

Frontier HTML Basics

Getting Comfortable With Outlines

Templates

Outline Formatting

Includes and Macros

Handling Images

Glossaries and Filters

Defines and Custom Directives

Publishing

Site Outline and NextPrev

Relative References

Leveraging Your Work

Narrative of a Rendering

Where To Go From Here

Terms, Tips and Examples

The #define directive

If your page is an outline, including the situation where it is an outline being Included in another page, it can have a different sort of "private" glossary. This is accomplished by means of the #define directive.

It is easiest to explain this simply by giving an example. Suppose, at the start of your outline, you have lines that go:


   #define "jefferson"
      The God who gave us life gave us liberty at the same time.
      The hand of force may destroy but cannot disjoin them.

Then, every time in that outline you use what looks like a macro call, "{jefferson}" (without the quotes), the entire phrase will be substituted: "The God who gave us life gave us liberty at the same time. The hand of force may destroy but cannot disjoin them."

What the #define evaluates to is everything subordinate to its definition line. This can be as many lines of material as you like, which will all be run together into a single string without any HTML formatting being introduced into it.

(Putting it another way, lineation may be meaningful in an outline, depending on what renderer you're using, but the material in a #define is not considered to have any lineation.)

But of course the main part of the outline does have lineation, and this lineation generates HTML (depending on our renderer). So, if we are using the default renderer, which puts the appropriate <li> tag before every line, a line consisting of


   {jefferson}

will end up bulleted, at the appropriate level, a single item consisting of the entire phrase.

One use of this is to circumvent restrictions imposed by outlines and the outline renderers. Suppose, for example, we are rendering our outline with the default renderer, and we wish to have a bulleted item of considerable length: say, 600 characters.

However, Frontier outline lines are limited to 255 characters. What are we to do? If we split our material into several outline lines, Frontier will render that as several different bulleted items.

Okay, you could write your own renderer. But another way is to use the #define mechanism as a workaround.

A similar trick allows you to use the default outline renderer, yet have a bulleted item consisting of multiple paragraphs. (Do you see how to do it? It's left as an exercise for the reader!)

Passing information up and down the hierarchy

Although a #define call can appear only in an outline, it may be referenced in any type of document. That's right: you can use the #define call to send information from one document to another!

For instance, let's suppose your template is an outline. Then that outline could contain a #define directive. If what is defined is called "jefferson," then a {jefferson} call will work in any page which uses that template, even if the page itself is not an outline!

So we've used a #define to pass information down the hierarchy.

More common in practical usage is to take advantage of the #define mechanism to pass information up the hierarchy. In particular, it is often used to hand a template a piece of information which will change for every page rendered with that template.

Let us suppose, for example, that every page is to have a title, possibly different from its titlebar #title, which is to appear in large colored letters at the beginning of the page -- that is, before the {bodytext}.

Since this is to be true of every page, and since it happens outside the {bodytext}, it makes sense to give the job of inserting this large colored title to the template. That way, if we later decide we want a small non-colored title instead, we have but to change the HTML in the template, once, and render our pages again.

So, we might have a line in the template just before the {bodytext} call, that looks like this:


   <center>   <h1><font size="7" color="#cc9900">{myTitle}</font></h1>   </center>

Then, in every (outline) page that is to be rendered with that template, we have a line


   #define "myTitle"

to which is subordinated a line with the desired title (not in quotes). For instance, it might be:


   #define "myTitle"
      This Is A Cool Title

As each page is rendered, the {myTitle} call in the template is replaced by its value as given in the body of the page. We have handed the value upwards from the page to the template that contains it. Try this yourself with fourthPage, if you like.

Custom directives

Now, you might say: "But the #define mechanism works only for outlines! What if I want to use this method when my pages are not outlines?" The solution is to use a custom directive.

It turns out that you are always free to make up your own directive. If you have a line in your page that says:


   #myTitle "This Is A Cool Title"

then Frontier will not complain on the grounds that it knows of no directive "#myTitle." On the contrary, it will do with "#myTitle" what it does with every directive.

Just what does Frontier do with directives? As it prepares to render a page, it sucks out the directives in the site table and in the page itself and stores them in a special table. We don't necessarily know the location of this special table, but when macros are being rendered we have a pointer to this special table at html.data.adrPageTable.

Often, this table will be at websites.#data. (In proper Frontier notation, that's written as websites.["#data"] -- since # is an otherwise illegal character, the table name needs to be enclosed in square braces and quotes.) After you preview or publish a page you can look at websites.["#data"] to see exactly what Frontier put in this special page table.

So, in this case, Frontier will just create an entry in the page table called "myTitle" and give it the value you said to give it, in this case the string "This Is A Cool Title."

The only difference between this directive and one of the pre-defined directives is that Frontier is not set up to do anything with this information -- that's up to you.

One way to do something with the value of a custom directive is to call it with the same macro-like syntax we used with #define. In this case, any occurrence of {myTitle} in your page would be replaced by "This Is A Cool Title" (without the quotes).

Why does this work? It's because (take a deep breath, now) a macro call is just a line of UserTalk, and a line of UserTalk can be the name of a database entry, and one of the places Frontier searches for database entries mentioned in macro calls is in the page table (we talked about this under Includes and Macros, remember?). So Frontier finds "myTitle" as an entry in the page table, and substitutes its value, which is "This Is A Cool Title." It would also be possible to say {html.data.adrPageTable^.myTitle} -- but why bother?

And #define works the same way: it, too, places an entry into the page table, which will be available to these macro-like calls up and down the hierarchy as the page is rendered.

Indeed, you could just as well use a custom directive in your outline instead of a #define, except that since outline lines are limited to 255 characters you wouldn't be able to define a longer string that way.

Custom directives and HTML tags

At this point I can picture you drooling at the thought of how you'll use custom directives to help generate HTML. But watch out! HTML tags (things in angle brackets) are protected from macro processing, remember? So you have to be clever to get the HTML you want.

One reader wrote me describing a problem along these lines. His Web pages are tables. There are a few cells at the top and bottom of each table that are common to each page, so obviously that's template material. The start and end of the table HTML is in the template; just the middle of the table is in each individual page.

But there's a problem. Different pages have different numbers of columns. The stuff in the template needs to span all those columns, with a tag like this:


   <td colspan=??? height=36>

The question is: how will the template know what to put for ??? for any particular page?

The answer: a custom directive! In each page, he can say how many columns its table has, like this:


   #numColumns "3"

That information is handed up to the template, which can use it to generate the HTML, like this, right?


   <td colspan={numColumns} height=36>

Wrong! A macro call inside angle brackets won't be seen.

Fortunately there are lots of solutions. Here's one; just remove the protection:


   \<td colspan={numColumns} height=36>

Here's another; generate the whole tag:


   {"<td colspan=" + numColumns + " height=36>"}

Or, generate each piece of the tag separately:


   {"<td colspan="}\{numColumns} \{"height=36>"}

Tricky stuff

This business of being able to get at the value of a directive by naming it in curly braces is very cool. But things can get rather complicated because of the way pages are embedded within one another: a page in a template, perhaps an Included page in that page, and so on.

A directive high up in the hierarchy, say in a template, is visible all the way down the hierarchy, into a page rendered with that template, then into an object rendered inside that page with {renderObject}, and so on. That's because all directives are gathered before any macro calls are interpreted.

But a custom directive inside an Included page is visible outside its own page only in what physically comes after the {renderObject} call that Includes it!

Suppose, for example, you have no definition of #myTitle in mySecondPage, but you do have a definition for it in myThirdPage, which is Included in mySecondPage. And suppose, in your template, you have the phrase "{myTitle}" (without the quotes) somewhere before the {bodytext} call and also somewhere after the {bodytext} call.

What do you think will happen when you view mySecondPage in the browser? Try it! The instance of "{myTitle}" at the top has generated an error, but the instance of "{myTitle}" at the bottom has been successfully replaced by your value for #myTitle!

This happened because {renderObject} is a macro call, and so is {myTitle}. When the first {myTitle} was first evaluated, we hadn't come to {renderObject} yet, so no entry for "myTitle" had gone into the page table, and this gave an error.

But when {renderObject} was encountered, Frontier started working on myThirdPage, and added its directives to the page table -- where they remained, so that when the later instance of {myTitle} was reached, there was a value to replace it with.

You're probably thinking about now: "I bet this is a problem that can be solved by asking for a second round of macro processing." But no: when we do the second round, the first {myTitle} is gone -- it's been replaced by the error message!

But there is a solution...

Getting Directives from Another Page

We're going to use a macro script to get us out of this jam. There is a solution that involves a default directive (and a second round of macro processing after all) but that second round of macro processing can cause headaches: URLs may be made active twice, etc. So it's always best to avoid a second round of macro processing.

Here's a macro script that will get all the directives from an object other than the page being rendered. You don't necessarily have to include that remote page, but most of the time you're probably going to.

Remember: this macro is not needed unless you will be referring to directives in the remote page before your renderObject macro call.

You don't have to understand the script, just be able to type it as it appears. It should go in your websites.myFirstSite.#tools table. The #tools table is a great place to put macros. You can also put them in user.html.macros when you expect to use a macro in several different sites.

(One thing to watch out for in typing this: there should be no spaces after the backslash in the highlighted line. That backslash is a line-continuation character, and it must be the last character in that line. You won't be able to compile the script if that backslash isn't the last character in the line.)

Put a line near the top of your template:


   {getDirectives (includedPage)}

That tells the page to get the directives from the remote page before doing any other macro processing.

After that line, you can refer to any of the directives in the included page anywhere in your template or page text.

Include a directive in mySecondPage:


   #includedPage "websites.myFirstSite.thirdPage"

That tells the template which is the page to get the directives from.

And later in mySecondPage you'll have your renderObject call. It could look like this:


   {renderObject (@websites.myFirstSite.thirdPage)}

Or it could look like this:


   {renderObject (includedPage)}

You might not want to get directives from another object for every page on your site. In that case, you may want to modify the getDirectives macro to do nothing when the includedPage is empty.

To do this, put a default #includedPage directive at websites.myFirstSite.#prefs.includedPage. It should be "", the empty string.

Then, in the getDirectives macro, place a line right below the local statement (near the top) like this:


   if adrObject == ""
      return ("")

The variable adrObject contains what the includedPage directive contains. If it's "", then just return "" right away, don't do anything special.

PreviousNext

   

Site Scripted By Frontier © Copyright 1996-98 UserLand Software. This page was last built on 6/17/98; 2:47:52 PM. It was originally posted on 7/4/97; 7:26:19 AM. Webmaster: brent@userland.com.

 
This tutorial was adapted for Frontier 5 by Brent Simmons, from the Frontier 4 web tutorial written by Matt Neuburg.